/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef USE_HI_MPI

#include <iostream>
#include <memory>
#include "acl/dvpp/hi_dvpp.h"
#include "acl/acl.h"
#include "acl/acl_rt.h"
#include "Log/Log.h"
#include "CommonDataType/CommonDataType.h"
#include "DvppCommon.h"

// vpc
APP_ERROR DvppCommon::HiInitVpc(void)
{
    APP_ERROR ret = hi_mpi_sys_init();
    if (ret != APP_ERR_OK) {
        LogError << "Failed to initialize hi_mpi_sys, ret = " << ret << ".";
        return ret;
    }
    
    hi_vpc_chn_attr stChnAttr;
    
    while ((unsigned int)chnId_ < MAX_CHANNEL_NUM) {
        ret = hi_mpi_vpc_create_chn(chnId_, &stChnAttr);
        if((unsigned int)ret == HI_ERR_VPC_EXIST) {
            LogInfo << "vpc channel " << chnId_ << " has been occupied.";
            chnId_ += 1;
            if (chnId_ == MAX_CHANNEL_NUM) {
                LogError << "All vpc channel are occupied.";
                return ret;
            }
            continue;
        } else if (ret == APP_ERR_OK) {
            LogInfo << "Create vpc channel successfully, channel id is " << chnId_;
            break;
        } else {
            LogError << "Failed to create vpc channel, ret = " << ret << ".";
            return ret;
        }
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiDeInitVpc()
{
    APP_ERROR ret = hi_mpi_vpc_destroy_chn(chnId_);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to destroy vpc channel, ret = " << ret << ".";
        return ret;
    }
    
    ret = hi_mpi_sys_exit();
    if (ret != APP_ERR_OK) {
        LogError << "Failed to exit hi_mpi_sys, ret = " << ret << ".";
        return ret;
    }
    
    return APP_ERR_OK;
}

/*
 * @description: Get the size of buffer used to save image for VPC according to width, height and format
 * @param  width specifies the width of the output image
 * @param  height specifies the height of the output image
 * @param  format specifies the format of the output image
 * @param: vpcSize is used to save the result size
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::HiGetVpcDataSize(uint32_t width, uint32_t height, hi_pixel_format format, uint32_t &vpcSize)
{
    // Check the invalid format of VPC function and calculate the output buffer size
    if (format != HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
        LogError << "Format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    uint32_t widthStride = DVPP_ALIGN_UP(width, VPC_WIDTH_ALIGN);
    uint32_t heightStride = DVPP_ALIGN_UP(height, VPC_HEIGHT_ALIGN);
    widthStride = (widthStride < MIN_VPC_OUTPUT_WIDTH_STRIDE) ? MIN_VPC_OUTPUT_WIDTH_STRIDE : widthStride;
    vpcSize = widthStride * heightStride * YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2;
    return APP_ERR_OK;
}

/*
 * @description: Get the aligned width and height of the output image according to the image format
 * @param: width specifies the width before alignment
 * @param: height specifies the height before alignment
 * @param: format specifies the image format
 * @param: widthStride is used to save the width after alignment
 * @param: heightStride is used to save the height after alignment
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::HiGetVpcOutputStrideSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                             uint32_t &widthStride, uint32_t &heightStride)
{
    // Check the invalidty of output format and calculate the output width and height
    if (format != HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
        LogError << "Output format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    widthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH);
    heightStride = DVPP_ALIGN_UP(height, VPC_STRIDE_HEIGHT);
    widthStride = (widthStride < MIN_VPC_OUTPUT_WIDTH_STRIDE) ? MIN_VPC_OUTPUT_WIDTH_STRIDE : widthStride;
    return APP_ERR_OK;
}

/*
 * @description: Get the aligned width and height of the input image according to the image format
 * @param: width specifies the width before alignment
 * @param: height specifies the height before alignment
 * @param: format specifies the image format
 * @param: widthStride is used to save the width after alignment
 * @param: heightStride is used to save the height after alignment
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::HiGetVpcInputStrideSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                              uint32_t &widthStride, uint32_t &heightStride)
{
    uint32_t inputWidthStride;
    // Check the invalidty of input format and calculate the input width stride
    if (format >= HI_PIXEL_FORMAT_YUV_400 && format <= HI_PIXEL_FORMAT_YVU_SEMIPLANAR_444) {
        // If format is YUV SP, keep widthStride not change.
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH);
    } else if (format >= HI_PIXEL_FORMAT_YUYV_PACKED_422 && format <= HI_PIXEL_FORMAT_VYUY_PACKED_422) {
        // If format is YUV422 packed, image size = H x W * 2;
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * YUV422_WIDTH_NU;
    } else if (format >= HI_PIXEL_FORMAT_YUV_PACKED_444 && format <= HI_PIXEL_FORMAT_BGR_888) {
        // If format is YUV444 packed or RGB, image size = H x W * 3;
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * YUV444_RGB_WIDTH_NU;
    } else if (format >= HI_PIXEL_FORMAT_ARGB_8888 && format <= HI_PIXEL_FORMAT_BGRA_8888) {
        // If format is XRGB8888, image size = H x W * 4
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * XRGB_WIDTH_NU;
    } else {
        LogError << "Input format[" << format << "] for VPC is invalid, please check it.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    uint32_t inputHeightStride = DVPP_ALIGN_UP(height, VPC_STRIDE_HEIGHT);
    // Check the input validity width stride.
    if (inputWidthStride > MAX_RESIZE_WIDTH_INPUT || inputWidthStride < MIN_RESIZE_WIDTH_INPUT) {
        LogError << "Input width stride " << inputWidthStride << " is invalid, not in [" << MIN_RESIZE_WIDTH_INPUT \
                 << ", " << MAX_RESIZE_WIDTH_INPUT << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    // Check the input validity height stride.
    if (inputHeightStride > MAX_RESIZE_HEIGHT_INPUT || inputHeightStride < MIN_RESIZE_HEIGHT_INPUT) {
        LogError << "Input height stride " << inputHeightStride << " is invalid, not in [" << MIN_RESIZE_HEIGHT_INPUT \
                 << ", " << MAX_RESIZE_HEIGHT_INPUT << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    widthStride = inputWidthStride;
    heightStride = inputHeightStride;
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVpcResize(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                                  VpcProcessType processType)
{
    // create information struct for input image
    hi_vpc_pic_info inputPic;
    inputPic.picture_width = DVPP_ALIGN_UP(input.width, HI_VPC_STRIDE_WIDTH);
    inputPic.picture_height = DVPP_ALIGN_UP(input.height, HI_VPC_STRIDE_HEIGHT);
    inputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    inputPic.picture_width_stride = input.widthStride;
    inputPic.picture_height_stride = input.heightStride;
    inputPic.picture_buffer_size = input.dataSize;
    inputPic.picture_address = input.data;
    // create information struct for output image
    hi_vpc_pic_info outputPic;
    outputPic.picture_width = output.width;
    outputPic.picture_height = output.height;
    outputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    outputPic.picture_width_stride = output.widthStride;
    outputPic.picture_height_stride = output.heightStride;
    outputPic.picture_buffer_size = output.dataSize;
    outputPic.picture_address = output.data;
    // employ resize interface
    uint32_t taskID = 0;
    APP_ERROR ret = hi_mpi_vpc_resize(chnId_, &inputPic, &outputPic, 0, 0, 0, &taskID, -1);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vpc_resize failed with ret:" << ret << ".";
        hi_mpi_dvpp_free(inputPic.picture_address);
        inputPic.picture_address = nullptr;
        hi_mpi_dvpp_free(outputPic.picture_address);
        outputPic.picture_address = nullptr;
        return HI_FAILURE;
    }
    // gain resize result
    uint32_t taskIDResult = taskID;
    ret = hi_mpi_vpc_get_process_result(chnId_, taskIDResult, -1);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vpc_get_process_result failed with ret:" << ret << ".";
        hi_mpi_dvpp_free(inputPic.picture_address);
        inputPic.picture_address = nullptr;
        return HI_FAILURE;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiCombineResizeProcess(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                                             VpcProcessType processType)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_) {
        LogError << "CombineResizeProcess cannot be called by the DvppCommon object which is initialized with " \
                 << "InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }
    // Check whether the image format and zoom ratio meet the requirements
    APP_ERROR ret = CheckResizeParams(input, output);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    // Get widthStride and heightStride for input and output image according to the format
    ret = HiGetVpcInputStrideSize(input.width, input.height, input.format, input.widthStride,
                                  input.heightStride);
    if (ret != APP_ERR_OK) {
        return ret;
    }

    resizedImage_ = std::make_shared<DvppDataInfo>();
    resizedImage_->width = DVPP_ALIGN_UP(output.width, HI_VPC_STRIDE_WIDTH);
    resizedImage_->height = DVPP_ALIGN_UP(output.height, HI_VPC_STRIDE_HEIGHT);
    resizedImage_->format = output.format;
    // Get the aligned width and height of the output image according to the image format
    ret = HiGetVpcOutputStrideSize(output.width, output.height, output.format, resizedImage_->widthStride,
                                   resizedImage_->heightStride);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    // Get output buffer size for resize output
    ret = HiGetVpcDataSize(output.width, output.height, output.format, resizedImage_->dataSize);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    // Malloc buffer for output of resize module
    // Need to pay attention to release of the buffer
    ret = hi_mpi_dvpp_malloc(deviceId_, (void **)(&(resizedImage_->data)), resizedImage_->dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc " << resizedImage_->dataSize << " bytes on dvpp for resize, ret = " << ret << ".";
        return ret;
    }

    aclrtMemset(resizedImage_->data, resizedImage_->dataSize, YUV_GREYER_VALUE, resizedImage_->dataSize);
    resizedImage_->frameId = input.frameId;
    ret = HiVpcResize(input, *resizedImage_, withSynchronize, processType);
    if (ret != APP_ERR_OK) {
        // Release the output buffer when resize failed, otherwise release it after use
        RELEASE_DVPP_DATA(resizedImage_->data);
    }
    return ret;
}

APP_ERROR DvppCommon::HiVpcCrop(const DvppCropInputInfo &cropInput, const DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_) {
        LogError << "VpcCrop cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }
    // Set dvpp picture description info of input image
    hi_vpc_pic_info inputPic;
    inputPic.picture_width = DVPP_ALIGN_UP(cropInput.dataInfo.width, HI_VPC_STRIDE_WIDTH);
    inputPic.picture_height = DVPP_ALIGN_UP(cropInput.dataInfo.height, HI_VPC_STRIDE_HEIGHT);
    inputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    inputPic.picture_width_stride = cropInput.dataInfo.widthStride;
    inputPic.picture_height_stride = cropInput.dataInfo.heightStride;
    inputPic.picture_buffer_size = cropInput.dataInfo.dataSize;
    inputPic.picture_address = cropInput.dataInfo.data;
    // Set dvpp picture description info of output image
    hi_vpc_pic_info outputPic;
    outputPic.picture_width = DVPP_ALIGN_UP(output.width, VPC_OUTPUT_WIDTH_ALIGN);
    outputPic.picture_height = DVPP_ALIGN_UP(output.height, VPC_OUTPUT_HEIGHT_ALIGN);
    outputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    outputPic.picture_width_stride = output.widthStride;
    outputPic.picture_height_stride = output.heightStride;
    outputPic.picture_buffer_size = output.dataSize;
    outputPic.picture_address = output.data;
    APP_ERROR ret = HiCropProcess(inputPic, outputPic, cropInput.roi, withSynchronize);
    if (ret != APP_ERR_OK) {
        LogError << "HiCropProcess failed with ret:" << ret << ".";
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiCropProcess(hi_vpc_pic_info inputPic, hi_vpc_pic_info &outputPic,
                                    const CropRoiConfig &cropArea, bool withSynchronize)
{
    APP_ERROR ret = aclrtMemset(outputPic.picture_address,
                                outputPic.picture_buffer_size, 0, outputPic.picture_buffer_size);
    hi_vpc_crop_region_info cropRegionInfos[1];
    cropRegionInfos[0].dest_pic_info = outputPic;
    cropRegionInfos[0].crop_region.left_offset = cropArea.left;
    cropRegionInfos[0].crop_region.top_offset = cropArea.up;
    cropRegionInfos[0].crop_region.crop_width = outputPic.picture_width;
    cropRegionInfos[0].crop_region.crop_height = outputPic.picture_height;
    // employ crop interface
    uint32_t taskID = 0;
    ret = hi_mpi_vpc_crop(chnId_, &inputPic, cropRegionInfos, 1, &taskID, -1);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vpc_crop failed with ret:" << ret << ".";
        hi_mpi_dvpp_free(inputPic.picture_address);
        inputPic.picture_address = nullptr;
        return ret;
    }
    uint32_t taskIDResult = taskID;
    ret = hi_mpi_vpc_get_process_result(chnId_, taskIDResult, -1);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vpc_get_process_result failed with ret:" << ret << ".";
        hi_mpi_dvpp_free(inputPic.picture_address);
        inputPic.picture_address = nullptr;
        return ret;
    }

    ret = HiGetNotAlignBuffer(outputPic);
    if (ret != APP_ERR_OK) {
        LogError << "HiGetNotAlignBuffer failed with ret:" << ret << ".";
        hi_mpi_dvpp_free(inputPic.picture_address);
        inputPic.picture_address = nullptr;
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiGetNotAlignBuffer(hi_vpc_pic_info &outputPic)
{
    uint32_t width = outputPic.picture_width;
    uint32_t height = outputPic.picture_height;
    uint32_t widthStride = outputPic.picture_width_stride;
    uint32_t heightStride = outputPic.picture_height_stride;
    uint8_t* alignBuffDev = (uint8_t*)outputPic.picture_address;
    uint32_t buffSizeNotAlign = width * height * YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2;
    cropImage_ = std::make_shared<DvppDataInfo>();
    cropImage_->dataSize = buffSizeNotAlign;
    APP_ERROR ret = hi_mpi_dvpp_malloc(deviceId_, (void **)&cropImage_->data, cropImage_->dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc memory on dvpp, ret = " << ret << ".";
        hi_mpi_dvpp_free(cropImage_->data);
        cropImage_->data = nullptr;
        return ret;
    }
    for (unsigned int i = 0; i < height; i++) {
        ret = aclrtMemcpy(cropImage_->data + i * width, width,
            alignBuffDev + i * widthStride, width, ACL_MEMCPY_DEVICE_TO_DEVICE);
        if (ret != APP_ERR_OK) {
            LogError << "AclrtMemcpy Failed, ret = " << ret << ".";
            hi_mpi_dvpp_free(alignBuffDev);
            hi_mpi_dvpp_free(cropImage_->data);
            cropImage_->data = nullptr;
            return ret;
        }
    }
    for (unsigned int i = 0; i < height / NOT_ALIGN_DIVISOR; i++) {
        ret = aclrtMemcpy(cropImage_->data + i * width + width * height, width,
            alignBuffDev + i * widthStride + widthStride * heightStride,
            width, ACL_MEMCPY_DEVICE_TO_DEVICE);
        if (ret != APP_ERR_OK) {
            LogError << "AclrtMemcpy Failed, ret = " << ret << ".";
            hi_mpi_dvpp_free(alignBuffDev);
            hi_mpi_dvpp_free(cropImage_->data);
            cropImage_->data = nullptr;
            return ret;
        }
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiCombineCropProcess(DvppCropInputInfo &input, DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_) {
        LogError << "CombineCropProcess cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }
    // Get widthStride and heightStride for input and output image according to the format
    APP_ERROR ret = HiGetVpcInputStrideSize(input.dataInfo.width, input.dataInfo.height, input.dataInfo.format,
        input.dataInfo.widthStride, input.dataInfo.heightStride);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    ret = CheckCropParams(input);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    cropImage_ = std::make_shared<DvppDataInfo>();
    cropImage_->width = output.width;
    cropImage_->height = output.height;
    cropImage_->format = output.format;
    ret = HiGetVpcOutputStrideSize(output.width, output.height, output.format, cropImage_->widthStride,
                                   cropImage_->heightStride);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    // Get output buffer size for resize output
    ret = HiGetVpcDataSize(output.width, output.height, output.format, cropImage_->dataSize);
    if (ret != APP_ERR_OK) {
        return ret;
    }

    // Malloc buffer for output of resize module
    // Need to pay attention to release of the buffer
    ret = hi_mpi_dvpp_malloc(deviceId_, (void **)(&(cropImage_->data)), cropImage_->dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc " << cropImage_->dataSize << " bytes on dvpp for resize, ret = " << ret << ".";
        return ret;
    }
    cropImage_->frameId = input.dataInfo.frameId;
    ret = HiVpcCrop(input, *cropImage_, withSynchronize);
    if (ret != APP_ERR_OK) {
        // Release the output buffer when resize failed, otherwise release it after use
        RELEASE_DVPP_DATA(cropImage_->data);
    }
    return ret;
}

/*
 * @description: Check whether the image format and zoom ratio meet the requirements
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::CheckResizeParams(const DvppDataInfo &input, const DvppDataInfo &output) const
{
    if (output.format != HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420 && output.format != HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
        LogError << "Output format[" << output.format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (output.height < MIN_RESIZE_HEIGHT_OUTPUT || output.height > MAX_RESIZE_HEIGHT_OUTPUT) {
        LogError << "Resize output height should be in range [6, 4096], which is " << output.height << ".";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (output.width < MIN_RESIZE_WIDTH_OUTPUT || output.width > MAX_RESIZE_WIDTH_OUTPUT) {
        LogError << "Resize output width should be in range [10, 4096], which is " << output.width << ".";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (((float)output.height / input.height) < MIN_RESIZE_SCALE ||
        ((float)output.height / input.height) > MAX_RESIZE_SCALE) {
        LogError << "Height resize scale should be in range [1/32, 16], which is " \
        << ((float)output.height / input.height) << ".";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (((float)output.width / input.width) < MIN_RESIZE_SCALE ||
        ((float)output.width / input.width) > MAX_RESIZE_SCALE) {
        LogError << "Width resize scale should be in range [1/32, 16], which is " \
        << ((float)output.width / input.width) << ".";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    return APP_ERR_OK;
}

/*
 * @description: Check whether the size of the cropped data and the cropped area meet the requirements
 * @param: input specifies the image information and the information about the area to be cropped
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::CheckCropParams(const DvppCropInputInfo &input) const
{
    APP_ERROR ret;
    uint32_t payloadSize;
    ret = HiGetVpcDataSize(input.dataInfo.widthStride, input.dataInfo.heightStride, HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420,
                           payloadSize);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    if (payloadSize != input.dataInfo.dataSize) {
        LogError << "Input data size: " << payloadSize << " to crop does not match input yuv image size: " \
                 << input.dataInfo.dataSize << ".";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    // Calculate crop width and height according to the input location
    uint32_t cropWidth = input.roi.right - input.roi.left + ODD_NUM_1;
    uint32_t cropHeight = input.roi.down - input.roi.up + ODD_NUM_1;
    if ((cropWidth < MIN_CROP_WIDTH) || (cropHeight < MIN_CROP_HEIGHT)) {
        LogError << "Crop area width:" << cropWidth << " need to be larger than 10 and height:" << cropHeight
                 << " need to be larger than 6.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    if ((input.roi.left + cropWidth > input.dataInfo.width) || (input.roi.up + cropHeight > input.dataInfo.height)) {
        LogError << "Target rectangle start location(" << input.roi.left << "," << input.roi.up << ") with size("
                 << cropWidth << "," << cropHeight << ") is out of the input image(" << input.dataInfo.width << ","
                 << input.dataInfo.height << ") to be cropped.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    return APP_ERR_OK;
}

// decode jpeg
APP_ERROR DvppCommon::HiInitJpegd(int width, int height)
{
    APP_ERROR ret = hi_mpi_sys_init();
    if (ret != APP_ERR_OK) {
        LogError << "Failed to initialize himpi system, ret = " << ret;
        return ret;
    }
    LogInfo << "The himpi system is initialized successfully";
    // create channel
    hi_vdec_chn_attr chnAttr;
    chnAttr.type = HI_PT_JPEG;
    chnAttr.mode = HI_VDEC_SEND_MODE_FRAME;
    chnAttr.pic_width = width;
    chnAttr.pic_height = height;
    chnAttr.stream_buf_size = width * height;
    chnAttr.frame_buf_cnt = 0;
    chnAttr.frame_buf_size = 0;
    while ((unsigned int)chnId_ < MAX_CHANNEL_NUM) {
        ret = hi_mpi_vdec_create_chn(chnId_, &chnAttr);
        if ((unsigned int)ret == HI_ERR_VDEC_EXIST) {
            LogInfo << "vdec channel " << chnId_ << " has been occupied.";
            chnId_ += 1;
            if (chnId_ == MAX_CHANNEL_NUM) {
                LogError << "All vdec channel are occupied.";
                return ret;
            }
            continue;
        } else if (ret == APP_ERR_OK) {
            LogInfo << "Create vdec channel successfully.";
            break;
        } else {
            LogError << "Failed to create vdec channel, ret = " << ret << ".";
            return ret;
        }
    }
    // set channel parameter
    hi_vdec_chn_param chnParam;
    ret = hi_mpi_vdec_get_chn_param(chnId_, &chnParam);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_get_chn_param fail:" << ret;
        return ret;
    }
    chnParam.pic_param.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; // reserved parameter
    chnParam.display_frame_num = 0; // min num of frame decoded cached image, range: [0, 16] Default: 2
    ret = hi_mpi_vdec_set_chn_param(chnId_, &chnParam);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_set_chn_param fail:" << ret;
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiDeInitJpegd()
{
    hi_mpi_vdec_stop_recv_stream(chnId_);
    hi_mpi_vdec_destroy_chn(chnId_);
    APP_ERROR ret = hi_mpi_sys_exit();
    if (ret != APP_ERR_OK) {
        LogError << "Failed to exit hi_mpi system, ret = " << ret;
    } else {
        LogInfo << "Exit hi_mpi system successfully.";
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiJpegDecode(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_) {
        LogError << "JpegDecode cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }
    // start receive stream
    APP_ERROR ret = hi_mpi_vdec_start_recv_stream(chnId_);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_start_recv_stream fail:" << ret;
        return ret;
    }
    // get input image information
    hi_vdec_stream stStream {};
    hi_vdec_pic_info outPicInfo {};
    ret = HiGetJpegdOutputInfo(input, outPicInfo, stStream);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    stStream.addr = (uint8_t *)input.data;
    // send stream
    outPicInfo.vir_addr = (uint64_t)output.data;
    ret = hi_mpi_vdec_send_stream(chnId_, &stStream, &outPicInfo, TIME_OUT);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_send_stream fail:" << ret;
        return ret;
    }
    // gain decode result
    hi_video_frame_info frame {};
    hi_vdec_stream stream {};
    hi_vdec_supplement_info stSupplement {};
    ret = hi_mpi_vdec_get_frame(chnId_, &frame, &stSupplement, &stream, TIME_OUT);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_get_frame failed with ret:" << ret << ".";
        return ret;
    }
    decodedImage_->data = (uint8_t *)frame.v_frame.virt_addr[0];
    decodedImage_->width = outPicInfo.width;
    decodedImage_->height = outPicInfo.height;
    decodedImage_->widthStride = outPicInfo.width_stride;
    decodedImage_->heightStride = outPicInfo.height_stride;

    // release frame
    ret = hi_mpi_vdec_release_frame(chnId_, &frame);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_release_frame failed with ret:" << ret << ".";
        return ret;
    }
    int32_t jpegdResult = frame.v_frame.frame_flag;
    if (jpegdResult != 0) {
        LogError << "Decode image failed";
        return APP_ERR_ACL_FAILURE;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiGetJpegDecodeDataSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                              uint32_t &decSize)
{
    hi_pic_buf_attr buf_attr {
        width, height, 0,
        HI_DATA_BIT_WIDTH_8,
        format,
        HI_COMPRESS_MODE_NONE
    };
    hi_u32 outputSize = hi_vdec_get_pic_buf_size(HI_PT_JPEG, &buf_attr);
    if (outputSize == 0) {
        LogError << "Failed to predict decode size of jpeg image, outputSize = " << outputSize << ".";
        return APP_ERR_ACL_FAILURE;
    }
    decSize = outputSize;
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiCombineJpegdProcess(const RawData& imageInfo, hi_pixel_format format, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_) {
        LogError << "CombineJpegdProcess cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }
    int32_t components;
    inputImage_ = std::make_shared<DvppDataInfo>();
    inputImage_->format = format;
    // Get picture width and height and number of channels from image data
    APP_ERROR ret = GetJpegImageInfo(imageInfo.data.get(), imageInfo.lenOfByte, inputImage_->width, inputImage_->height,
                                     components);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to get input image info, ret = " << ret << ".";
        return ret;
    }
    // Get the buffer size of decode output according to the width and height and output format
    uint32_t outBuffSize;
    ret = HiGetJpegDecodeDataSize(inputImage_->width, inputImage_->height, format, outBuffSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to get size of decode output buffer, ret = " << ret << ".";
        return ret;
    }
    // In TransferImageH2D function, device buffer will be alloced to store the input image
    // Need to pay attention to release of the buffer
    ret = TransferImageH2D(imageInfo, inputImage_);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    decodedImage_ = std::make_shared<DvppDataInfo>();
    decodedImage_->format = format;
    decodedImage_->dataSize = outBuffSize;
    // Malloc dvpp buffer to store the output data after decoding
    // Need to pay attention to release of the buffer
    ret = hi_mpi_dvpp_malloc(deviceId_, (void **)&decodedImage_->data, decodedImage_->dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc memory on dvpp, ret = " << ret << ".";
        RELEASE_DVPP_DATA(inputImage_->data);
        return ret;
    }
    ret = HiJpegDecode(*inputImage_, *decodedImage_, withSynchronize);
    if (ret != APP_ERR_OK) {
        // Release the output buffer when decode failed, otherwise release it after use
        RELEASE_DVPP_DATA(inputImage_->data);
        inputImage_->data = nullptr;
        RELEASE_DVPP_DATA(decodedImage_->data);
        decodedImage_->data = nullptr;
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiGetJpegdOutputInfo(DvppDataInfo &input, hi_vdec_pic_info &outPicInfo, hi_vdec_stream &stStream)
{
    uint8_t* inBuffHost = nullptr;
    APP_ERROR ret = aclrtMallocHost((void **)&inBuffHost, input.dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc " << input.dataSize << " bytes on host, ret = " << ret << ".";
        return ret;
    }
    ret = aclrtMemcpy(inBuffHost, input.dataSize, input.data, input.dataSize,
                      ACL_MEMCPY_DEVICE_TO_HOST);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to copy " << input.dataSize << " bytes from device to host, ret = " << ret << ".";
        RELEASE_DVPP_DATA(inBuffHost);
        return ret;
    }
    stStream.pts = 0;
    stStream.addr = (uint8_t *)inBuffHost;
    stStream.len = input.dataSize;
    stStream.end_of_frame = HI_TRUE;
    stStream.end_of_stream = HI_FALSE;
    stStream.need_display = HI_TRUE;
    hi_img_info stImgInfo {};
    ret = hi_mpi_dvpp_get_image_info(HI_PT_JPEG, &stStream, &stImgInfo);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_dvpp_get_image_info failed with ret:" << ret << ".";
        RELEASE_DVPP_DATA(inBuffHost);
        return ret;
    }
    outPicInfo.width = stImgInfo.width;
    outPicInfo.height = stImgInfo.height;
    outPicInfo.width_stride = stImgInfo.width_stride;
    outPicInfo.height_stride = stImgInfo.height_stride;
    outPicInfo.buffer_size = stImgInfo.img_buf_size;
    outPicInfo.pixel_format = HI_PIXEL_FORMAT_UNKNOWN;
    return APP_ERR_OK;
}

// encode jpeg
APP_ERROR DvppCommon::CerateJpegeChannel(int width, int height)
{
    uint32_t bufSize = 0;
    if ((unsigned)width > JPEGE_CHAN_MIDDLE_WIDTH) {
        bufSize = ALIGN_UP(width * height * HI_VENC_CHAN_BUFF_SIZE_FACTOR_HIGH, HI_VENC_CHAN_BUFF_SIZE_ALIGN);
    } else {
        bufSize = ALIGN_UP(width * height * HI_VENC_CHAN_BUFF_SIZE_FACTOR_LOW, HI_VENC_CHAN_BUFF_SIZE_ALIGN);
    }
    bufSize = (bufSize < HI_VENC_MIN_CHAN_BUFF_SIZE) ? HI_VENC_MIN_CHAN_BUFF_SIZE : bufSize;
    hi_venc_chn_attr vencChnAttr;
    vencChnAttr.venc_attr.type = HI_PT_JPEG;
    vencChnAttr.venc_attr.profile = 0;
    vencChnAttr.venc_attr.max_pic_width = width;
    vencChnAttr.venc_attr.max_pic_height = height;
    vencChnAttr.venc_attr.pic_width = width;
    vencChnAttr.venc_attr.pic_height = height;
    vencChnAttr.venc_attr.buf_size = bufSize; // multiple of 64, recommend: max_pic_width * max_pic_height * 1.5
    vencChnAttr.venc_attr.is_by_frame = HI_TRUE; // get stream mode is field mode or frame mode
    vencChnAttr.venc_attr.jpeg_attr.dcf_en = HI_FALSE;
    vencChnAttr.venc_attr.jpeg_attr.recv_mode = HI_VENC_PIC_RECV_SINGLE;
    vencChnAttr.venc_attr.jpeg_attr.mpf_cfg.large_thumbnail_num = 0;
    APP_ERROR ret = APP_ERR_OK;
    while ((unsigned int)chnId_ < MAX_CHANNEL_NUM) {
        ret = hi_mpi_venc_create_chn(chnId_, &vencChnAttr);
        if ((unsigned int)ret == HI_ERR_VENC_EXIST) {
            LogInfo << "venc channel " << chnId_ << " has been occupied.";
            chnId_ += 1;
            if (chnId_ == MAX_CHANNEL_NUM) {
                LogError << "All venc channel are occupied.";
                return ret;
            }
            continue;
        } else if (ret == APP_ERR_OK) {
            LogInfo << "Create venc channel successfully, channel id is " << chnId_;
            break;
        } else {
            LogError << "Failed to create venc channel, ret = " << ret << ".";
            return ret;
        }
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiInitJpege(int width, int height)
{
    APP_ERROR ret = hi_mpi_sys_init();
    if (ret != APP_ERR_OK) {
        LogError << "Hi_mpi_sys_init failed, ret = " << std::hex << ret << ".";
        return ret;
    }
    // set mod param
    hi_venc_mod_param modParam;
    modParam.mod_type = HI_VENC_MOD_JPEG;
    hi_mpi_venc_get_mod_param(&modParam);
    modParam.jpeg_mod_param.one_stream_buf = 1; // only support single-packet mode
    ret = hi_mpi_venc_set_mod_param(&modParam);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_venc_set_mod_param failed with ret:" << std::hex << ret;
        return ret;
    }
    // create venc channel
    ret = CerateJpegeChannel(width, height);
    if (ret != APP_ERR_OK) {
        LogError << "CerateJpegeChannel failed with ret:" << std::hex << ret;
        return ret;
    }
    // set jpeg param
    hi_venc_jpeg_param stParamJpeg;
    ret = hi_mpi_venc_get_jpeg_param(chnId_, &stParamJpeg);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_venc_get_jpeg_param failed with ret:" << std::hex << ret;
        hi_mpi_venc_destroy_chn(chnId_);
        return ret;
    }
    ret = hi_mpi_venc_set_jpeg_param(chnId_, &stParamJpeg);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_venc_set_jpeg_param failed with ret:" << std::hex << ret;
        hi_mpi_venc_destroy_chn(chnId_);
        return ret;
    }
    // start channel
    hi_venc_start_param recvParam;
    recvParam.recv_pic_num = -1; // unspecified frame count
    ret = hi_mpi_venc_start_chn(chnId_, &recvParam);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_venc_start_chn failed with ret:" << std::hex << ret;
        hi_mpi_venc_destroy_chn(chnId_);
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiDeInitJpege()
{
    APP_ERROR ret = hi_mpi_venc_stop_chn(chnId_);
    if (ret != APP_ERR_OK) {
         LogError << "hi_mpi_venc_stop_chn failed with ret:" << ret;
         return ret;
    }
    LogInfo << "venc chn stoped.";

    ret = hi_mpi_venc_destroy_chn(chnId_);
    if (ret != APP_ERR_OK) {
         LogError << "hi_mpi_venc_destroy_chn failed with ret:" << ret;
         return ret;
    }
    LogInfo << "venc chn destroyed.";

    ret = hi_mpi_sys_exit();
    if (ret != APP_ERR_OK) {
        LogError << "Hi_mpi_sys_exit failed, ret = " << ret << ".";
    }
    LogInfo << "Hi_mpi_sys_exit successfully.";

    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiCombineJpegeProcess(const RawData &imageInfo, uint32_t width, uint32_t height,
                                            hi_pixel_format format, bool withSynchronize)
{
    inputImage_ = std::make_shared<DvppDataInfo>();
    inputImage_->format = format;
    inputImage_->width = width;
    inputImage_->height = height;
    // Transfer data from host to device
    APP_ERROR ret = TransferImageH2D(imageInfo, inputImage_);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    // get stridewidth and strideheight of inputimage
    ret = GetJpegEncodeStrideSize(inputImage_);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to get encode stride size of input image file, ret = " << std::hex << ret << ".";
        hi_mpi_dvpp_free(inputImage_->data);
        inputImage_->data = nullptr;
        return ret;
    }
    // send frame
    ret = HiVencSendFrame(format);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to send frame, ret = " << std::hex << ret << ".";
        hi_mpi_dvpp_free(inputImage_->data);
        inputImage_->data = nullptr;
        return ret;
    }
    // get stream
    hi_venc_stream stream;
    ret = HiVencGetStream(stream);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to get stream, ret = " << std::hex << ret;
        hi_mpi_dvpp_free(inputImage_->data);
        inputImage_->data = nullptr;
        if (epollExist_ == true) {
            ret = CloseEpoll();
            if (ret != APP_ERR_OK) {
                LogError << "CloseEpoll failed with ret:" << std::hex << ret;
                return ret;
            }
        }
        return ret;
    }
    hi_mpi_dvpp_free(inputImage_->data);
    inputImage_->data = nullptr;

    ret = HiVencGetResult(stream);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to get result, ret = " << std::hex << ret;
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVencGetResult(hi_venc_stream &stream)
{
    encodedImage_ = std::make_shared<DvppDataInfo>();
    uint32_t dataLen = stream.pack->len - stream.pack->offset;
    encodedImage_->dataSize = dataLen;
    APP_ERROR ret = hi_mpi_dvpp_malloc(deviceId_, (void **)&encodedImage_->data, encodedImage_->dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc memory on dvpp, ret = " << std::hex << ret << ".";
        return ret;
    }
    ret = (APP_ERROR)aclrtMemcpy(encodedImage_->data, encodedImage_->dataSize, stream.pack->addr + stream.pack->offset,
                                 encodedImage_->dataSize, ACL_MEMCPY_DEVICE_TO_DEVICE);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to copy memory, ret = " << std::hex << ret << ".";
        hi_mpi_dvpp_free(encodedImage_->data);
        encodedImage_->data = nullptr;
        return ret;
    }
    ret = hi_mpi_venc_release_stream(chnId_, &stream);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to release stream, ret = " << std::hex << ret << ".";
        hi_mpi_dvpp_free(encodedImage_->data);
        encodedImage_->data = nullptr;
        return ret;
    }
    LogInfo << "Release stream successfully.";
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVencSendFrame(hi_pixel_format format)
{
    // send data and start encoding
    hi_video_frame_info *inputFrame = nullptr;
    inputFrame = (hi_video_frame_info *)malloc(sizeof(hi_video_frame_info));
    inputFrame->pool_id = 0; // Reserved parameter
    inputFrame->mod_id = HI_ID_VENC; // Reserved parameter
    inputFrame->v_frame.width = inputImage_->width;
    inputFrame->v_frame.height = inputImage_->height;
    inputFrame->v_frame.dynamic_range = HI_DYNAMIC_RANGE_SDR8; // Dynamic Range
    inputFrame->v_frame.compress_mode = HI_COMPRESS_MODE_NONE; // Compression Mode, only HI_COMPRESS_MODE_NONE
    inputFrame->v_frame.pixel_format = format; // Pixel format
    inputFrame->v_frame.video_format = HI_VIDEO_FORMAT_LINEAR; // Video format
    inputFrame->v_frame.field = HI_VIDEO_FIELD_FRAME; // Frame Or Field Mode ,Reserved parameter
    inputFrame->v_frame.color_gamut = HI_COLOR_GAMUT_BT709; // Gamut range ,Reserved parameter
    inputFrame->v_frame.header_stride[0] = 0; // Image compression head span
    inputFrame->v_frame.header_stride[1] = 0; // Reserved parameter
    inputFrame->v_frame.width_stride[0] = inputImage_->widthStride;
    inputFrame->v_frame.width_stride[1] = inputImage_->widthStride;
    inputFrame->v_frame.height_stride[0] = inputImage_->heightStride;
    inputFrame->v_frame.height_stride[1] = inputImage_->heightStride;
    inputFrame->v_frame.virt_addr[0] = inputImage_->data;
    inputFrame->v_frame.virt_addr[1] =
        (hi_void*)((uintptr_t)inputFrame->v_frame.virt_addr[0] +
        inputImage_->widthStride * inputImage_->heightStride);
    inputFrame->v_frame.time_ref = VENC_TIME_REF;
    inputFrame->v_frame.frame_flag = 0;
    APP_ERROR ret = hi_mpi_venc_send_frame(chnId_, inputFrame, TIME_OUT);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_venc_send_frame failed with ret:" << std::hex << ret << ".";
        free(inputFrame);
        return ret;
    }
    LogInfo << "Send_frame successfully.";
    free(inputFrame);
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVencGetStream(hi_venc_stream &stream)
{
    APP_ERROR ret = APP_ERR_OK;
    ret = CreateEpoll();
    if (ret != APP_ERR_OK) {
        LogError << "CreateEpoll failed with ret:" << std::hex << ret;
        epollExist_ = false;
        return ret;
    }
    int32_t eventCount = 0;
    hi_dvpp_epoll_event events[1024];
    ret = hi_mpi_sys_wait_epoll(epollFd_, events, EPOLL_MAX_EVENTS, TIME_OUT, &eventCount);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_sys_wait_epoll failed with ret:" << std::hex << ret;
        return ret;
    }
    hi_venc_chn_status stat;
    ret = hi_mpi_venc_query_status(chnId_, &stat);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_venc_query_status failed with ret:" << std::hex << ret;
        return ret;
    }
    stream.pack_cnt = stat.cur_packs;
    hi_venc_pack pack[stream.pack_cnt];
    stream.pack = pack;
    ret = hi_mpi_venc_get_stream(chnId_, &stream, TIME_OUT);
    if (ret != APP_ERR_OK) {
        LogError << "Get stream failed with ret:" << std::hex << ret;
        return ret;
    }
    LogInfo << "Get stream successfully.";
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::CreateEpoll()
{
    epollFd_ = 0;
    fd_ = hi_mpi_venc_get_fd(chnId_);
    APP_ERROR ret = hi_mpi_sys_create_epoll(EPOLL_MAX_NUM, &epollFd_);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_sys_create_epoll failed with ret:" << std::hex << ret;
        return ret;
    }
    hi_dvpp_epoll_event event;
    event.events = HI_DVPP_EPOLL_IN;
    event.data = (void*)(unsigned long)(fd_);
    ret = hi_mpi_sys_ctl_epoll(epollFd_, HI_DVPP_EPOLL_CTL_ADD, fd_, &event);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_sys_ctl_epoll failed with ret:" << std::hex << ret;
        hi_mpi_sys_close_epoll(epollFd_);
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::CloseEpoll()
{
    APP_ERROR ret = hi_mpi_sys_ctl_epoll(epollFd_, HI_DVPP_EPOLL_CTL_DEL, fd_, NULL);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_sys_ctl_epoll failed with ret:" << std::hex << ret;
        return ret;
    }
    ret = hi_mpi_sys_close_epoll(epollFd_);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_sys_ctl_epoll failed with ret:" << std::hex << ret;
        return ret;
    }
    return APP_ERR_OK;
}

// decode video
bool DvppCommon::WriteToFile(const std::string &fileName, uint8_t* dataDev, uint32_t dataSize)
{
    void* dataHost = nullptr;
    auto aclRet = aclrtMallocHost(&dataHost, dataSize);
    if (aclRet != APP_ERR_OK) {
        LogError << "malloc host data buffer failed. dataSize= " << dataSize << "\n";
        return false;
    }
    // copy output to host memory
    aclRet = aclrtMemcpy(dataHost, dataSize, dataDev, dataSize, ACL_MEMCPY_DEVICE_TO_HOST);
    if (aclRet != ACL_ERROR_NONE) {
        LogError << "acl memcpy data to host failed, dataSize= " << dataSize << "ret= " << aclRet << "\n";
        aclrtFreeHost(dataHost);
        return false;
    }
    FILE *outFileFp = fopen(fileName.c_str(), "wb+");
    if (outFileFp == nullptr) {
        LogError << "fopen out file failed " << fileName;
        aclrtFreeHost(dataHost);
        return false;
    }
    bool ret = true;
    size_t writeRet = fwrite(dataHost, 1, dataSize, outFileFp);
    if (writeRet != dataSize) {
        LogError << "need write " << dataSize << "bytes to " << fileName << "but only write " << writeRet;
        aclrtFreeHost(dataHost);
        fflush(outFileFp);
        fclose(outFileFp);
        ret = false;
    }
    aclrtFreeHost(dataHost);
    fflush(outFileFp);
    fclose(outFileFp);
    return ret;
}

void *VdecThread(void* arg)
{
    // Notice: Create context for this thread
    auto *dvppCommon = (DvppCommon*)arg;
    aclrtContext context = static_cast<aclrtContext>(dvppCommon->GetContext());
    APP_ERROR ret = aclrtSetCurrentContext(context);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to set context, ret = " << ret;
        return reinterpret_cast<void*>(-1);
    }
    LogInfo << "Vdec thread start, decoding......";
    static int count = 0;
    hi_video_frame_info frame;
    while (dvppCommon->GetRunFlag()) {
        bool decResult = true;
        ret = dvppCommon->HiVdecGetFrame(frame, decResult, count);
        if ((unsigned)ret == HI_ERR_VDEC_BUF_EMPTY || decResult == false) {
            continue;
        } else if ((unsigned)ret != HI_ERR_VDEC_BUF_EMPTY && ret != APP_ERR_OK) {
            LogError << "Vdec get frame failed, count = " << count << ", ret = " << std::hex << ret;
            return ((void *)(-1));
        }
        count++;
        LogInfo << "Finish geting the " << count << " th frame.";
    }
    LogInfo << "Get frames finish, " << count << " frames were get.";
    return nullptr;
}
   
APP_ERROR DvppCommon::PrepareToWrite(hi_video_frame_info frame, uint32_t count)
{
    // save decoded video
    uint32_t imageSize;
    APP_ERROR ret = HiGetVideoDecodeDataSize(frame.v_frame.width,
        frame.v_frame.height, vdecConfig_.outFormat, imageSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to get video decode data size, ret = " << std::hex << ret;
        return ret;
    }
    uint8_t* OutBufferDev = (uint8_t*)frame.v_frame.virt_addr[0];
    std::string fileNameSave = vdecConfig_.outputFolder + "/image" + std::to_string(count) + ".yuv";
    if (!WriteToFile(fileNameSave.c_str(), OutBufferDev, imageSize)) {
        LogError << "Failed to write file" << ", count=" << count;
        OutBufferDev = nullptr;
        return APP_ERR_ACL_ERR_BASE;
    }
    OutBufferDev = nullptr;
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::SetVdecChnParm()
{
    hi_vdec_chn_param chnParam;
    APP_ERROR ret = hi_mpi_vdec_get_chn_param(vdecConfig_.channelId, &chnParam);
    chnParam.video_param.dec_mode = HI_VIDEO_DEC_MODE_IPB; // decoding mode
    chnParam.video_param.compress_mode = HI_COMPRESS_MODE_HFBC; // Compression mode
    chnParam.video_param.video_format = HI_VIDEO_FORMAT_TILE_64x16;
    chnParam.video_param.out_order = HI_VIDEO_OUT_ORDER_DISPLAY;
    ret = hi_mpi_vdec_set_chn_param(vdecConfig_.channelId, &chnParam);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to set param of vdec channel, ret = " << ret << ".";
        return ret;
    }
    ret = hi_mpi_vdec_start_recv_stream(vdecConfig_.channelId);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to start recv stream of vdec channel, ret = " << ret << ".";
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::CreateVdecChannel()
{
    APP_ERROR ret = APP_ERR_OK;
    hi_vdec_chn_attr chnAttr;
    hi_pic_buf_attr buf_attr {
        (hi_u32)vdecConfig_.inputWidth, (hi_u32)vdecConfig_.inputHeight, 0,
        HI_DATA_BIT_WIDTH_8, HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420, HI_COMPRESS_MODE_NONE
    };
    if (vdecConfig_.inFormat == H264_MAIN_LEVEL) {
        chnAttr.type = HI_PT_H264;
    } else {
        chnAttr.type = HI_PT_H265;
    }
    chnAttr.mode = HI_VDEC_SEND_MODE_FRAME; // Stream sending mode
    chnAttr.pic_width = vdecConfig_.inputWidth;
    chnAttr.pic_height = vdecConfig_.inputHeight;
    chnAttr.stream_buf_size = vdecConfig_.inputHeight * vdecConfig_.inputHeight * 
        YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2;
    chnAttr.frame_buf_cnt = 0; // Number of decoded image frames
    chnAttr.frame_buf_size = hi_vdec_get_pic_buf_size(chnAttr.type, &buf_attr); // buff size of decoded image frame
    chnAttr.video_attr.ref_frame_num = REF_FRAME_NUM; // range: [0, 16]
    chnAttr.video_attr.temporal_mvp_en = HI_TRUE;
    chnAttr.video_attr.tmv_buf_size = hi_vdec_get_tmv_buf_size(chnAttr.type,
        vdecConfig_.inputWidth, vdecConfig_.inputHeight);
    while (vdecConfig_.channelId < MAX_CHANNEL_NUM) {
        ret = hi_mpi_vdec_create_chn(vdecConfig_.channelId, &chnAttr);
        if ((unsigned int)ret == HI_ERR_VDEC_EXIST) {
            LogInfo << "vdec channel " << vdecConfig_.channelId << " has been occupied.";
            vdecConfig_.channelId += 1;
            if (vdecConfig_.channelId == MAX_CHANNEL_NUM) {
                LogError << "All vdec channel are occupied.";
                return ret;
            }
            continue;
        } else if (ret == APP_ERR_OK) {
            LogInfo << "Create vdec channel successfully. chnid = " << vdecConfig_.channelId;
            break;
        } else {
            LogError << "Failed to create vdec channel, ret = " << std::hex << ret << ".";
            return ret;
        }
    }
    return APP_ERR_OK;   
}

APP_ERROR DvppCommon::HiInitVdec()
{
    APP_ERROR ret = CheckVdecConfig();
    if (ret != APP_ERR_OK) {
        LogError << "Invalid input parameters";
        return ret;
    }
    SetRunFlag(true);
    ret = hi_mpi_sys_init();
    if (ret != APP_ERR_OK) {
        LogError << "Failed to init hi_mpi system, ret = " << ret;
        return ret;
    }
    isVdec_ = true;
    ret = CreateVdecChannel();
    if (ret != APP_ERR_OK) {
        LogError << "CreateVdecChannel fail, ret = " << ret << ".";
        return ret;
    }
    ret = SetVdecChnParm();
    if (ret != APP_ERR_OK) {
        LogError << "SetVdecChnParm fail, ret = " << ret << ".";
        return ret;
    }
    int createThreadErr = pthread_create(&(vdecConfig_).threadId, nullptr, VdecThread,
        (void *)this);
    if (createThreadErr != 0) {
        LogError << "Failed to create thread, err = " << createThreadErr;
        return APP_ERR_COMM_INIT_FAIL;
    }
    LogInfo << "VdecThread successfully.";
    return APP_ERR_OK;
}
    
APP_ERROR DvppCommon::HiDeInitVdec()
{
    SetRunFlag(false);
    int joinThreadErr = pthread_join(vdecConfig_.threadId, NULL);
    if (joinThreadErr != 0) {
        LogError << "Failed to join thread, threadId = " << vdecConfig_.threadId << "err = " << joinThreadErr;
        return APP_ERR_ACL_FAILURE;
    }
    APP_ERROR ret = hi_mpi_vdec_stop_recv_stream(vdecConfig_.channelId);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_stop_recv_stream failed, ret = " << std::hex << ret << ".";
        return ret;
    }
    ret = hi_mpi_vdec_destroy_chn(vdecConfig_.channelId);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_destroy_chn failed, ret = " << std::hex << ret << ".";
        return ret;
    }
    ret = hi_mpi_sys_exit();
    if (ret != APP_ERR_OK) {
        LogError << "Hi_mpi_sys_exit failed, ret = " << std::hex << ret << ".";
        return ret;
    }
    LogInfo << "Hi_mpi_sys_exit successfully.";
    return APP_ERR_OK;
}
    
/*
 * @description: Get the aligned width and height of the output image after video decoding
 * @param: width specifies the width before alignment
 * @param: height specifies the height before alignment
 * @param: format specifies the format of the output image
 * @param: widthStride is used to save the width after alignment
 * @param: heightStride is used to save the height after alignment
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::HiGetVideoDecodeStrideSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                                 uint32_t &widthStride, uint32_t &heightStride)
{
    // Check the invalidty of output format and calculate the output width and height
    if (format != HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
        LogError << "Input format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    widthStride = (width < VDEC_SPECIAL_WIDTH) ? VDEC_SPECIAL_STRIDE : DVPP_ALIGN_UP(width, VDEC_STRIDE_WIDTH);
    heightStride = DVPP_ALIGN_UP(height, VDEC_STRIDE_HEIGHT);
    return APP_ERR_OK;
}
    
/*
 * @description: Get the buffer size for storing results after video decoding
 * @param  width specifies the width of the output image after video decoding
 * @param  height specifies the height of the output image after video decoding
 * @param  format specifies the format of the output image
 * @param: vpcSize is used to save the result size
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::HiGetVideoDecodeDataSize(uint32_t width, uint32_t height, hi_pixel_format format,
                                               uint32_t &vdecSize)
{
    // Check the invalid format of vdec output and calculate the output buffer size
    if (format != HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
        LogError << "Format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    uint32_t widthStride = DVPP_ALIGN_UP(width, VDEC_STRIDE_WIDTH);
    uint32_t heightStride = DVPP_ALIGN_UP(height, VDEC_STRIDE_HEIGHT);
    vdecSize = widthStride * heightStride * YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2;
    return APP_ERR_OK;
}   

APP_ERROR DvppCommon::HiCombineVdecProcess(std::shared_ptr<DvppDataInfo> data, void *userData)
{
    // Return special error code when the DvppCommon object is not initialized with InitVdec
    if (!isVdec_) {
        LogError << "CombineVdecProcess cannot be called by the DvppCommon object which is not initialized with " \
                 << "InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }
    void *picOutBufferDev = nullptr;
    DvppDataInfo dataInfo;
    APP_ERROR ret = HiGetVdecOutputInfo(picOutBufferDev, dataInfo);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to get vdec output information: " << std::hex << ret << ".";
        return ret;
    }
    void *InDevBuff = nullptr;
    ret = hi_mpi_dvpp_malloc(deviceId_, (void **)&InDevBuff, data->dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc dvpp data with " << data->dataSize << " bytes, ret = " << std::hex << ret << ".";
        hi_mpi_dvpp_free(picOutBufferDev);
        picOutBufferDev = nullptr;
        return APP_ERR_ACL_BAD_ALLOC;
    }
    ret = aclrtMemcpy(InDevBuff, data->dataSize, static_cast<uint8_t *>(data->data), data->dataSize,
                      ACL_MEMCPY_HOST_TO_DEVICE);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to copy memory with " << data->dataSize << " bytes from host to device, ret = " \
                 << std::hex << ret << ".";
        hi_mpi_dvpp_free(InDevBuff);
        InDevBuff = nullptr;
        hi_mpi_dvpp_free(picOutBufferDev);
        picOutBufferDev = nullptr;
        return APP_ERR_ACL_FAILURE;
    }
    // send frame
    ret = HiVdecSendStream(InDevBuff, data->dataSize, dataInfo);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to send frame with error code: " << std::hex << ret << ".";
        hi_mpi_dvpp_free(InDevBuff);
        InDevBuff = nullptr;
        hi_mpi_dvpp_free(picOutBufferDev);
        picOutBufferDev = nullptr;
        return APP_ERR_ACL_BAD_ALLOC;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiGetVdecOutputInfo(void *picOutBufferDev, DvppDataInfo &dataInfo)
{
    dataInfo.width = vdecConfig_.inputWidth;
    dataInfo.height = vdecConfig_.inputHeight;
    dataInfo.format = vdecConfig_.outFormat;
    APP_ERROR ret = HiGetVideoDecodeStrideSize(dataInfo.width, dataInfo.height,
        dataInfo.format, dataInfo.widthStride, dataInfo.heightStride);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to GetVideoDecodeStrideSize, ret = " << std::hex << ret << ".";
        return ret;
    }
    ret = HiGetVideoDecodeDataSize(vdecConfig_.inputWidth, vdecConfig_.inputHeight,
        vdecConfig_.outFormat, dataInfo.dataSize);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    ret = hi_mpi_dvpp_malloc(deviceId_, &picOutBufferDev, dataInfo.dataSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc memory with " << dataInfo.dataSize << " bytes, ret = " << std::hex << ret << ".";
        return APP_ERR_ACL_BAD_ALLOC;
    }
    dataInfo.data = static_cast<uint8_t*>(picOutBufferDev);
    
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVdecSendStream(void *InDevBuff, uint32_t inputDataSize,
    DvppDataInfo dataInfo)
{
    // creating input and output information struct
    hi_vdec_stream stream;
    stream.addr = (uint8_t *)InDevBuff;
    stream.len = inputDataSize;
    stream.end_of_frame = HI_TRUE;
    stream.end_of_stream = HI_FALSE;
    stream.need_display = HI_TRUE;

    hi_vdec_pic_info outPicInfo;
    outPicInfo.width = dataInfo.width;
    outPicInfo.height = dataInfo.height;
    outPicInfo.width_stride = dataInfo.widthStride;
    outPicInfo.height_stride = dataInfo.heightStride;
    outPicInfo.pixel_format = dataInfo.format;
    outPicInfo.vir_addr = (uint64_t)dataInfo.data;
    outPicInfo.buffer_size = dataInfo.dataSize;

    // send frame
    APP_ERROR ret = APP_ERR_OK;
    do {
        ret = hi_mpi_vdec_send_stream(vdecConfig_.channelId, &stream, &outPicInfo, TIME_OUT);
        if (ret != APP_ERR_OK && (unsigned)ret != HI_ERR_VDEC_BUF_FULL) {
            LogError << "Failed to send frame with error code: " << std::hex << ret << ".";
            return APP_ERR_ACL_BAD_ALLOC;
        }
    } while ((unsigned)ret == HI_ERR_VDEC_BUF_FULL);
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVdecSendEndStream()
{
    hi_vdec_stream stream;
    stream.addr = NULL;
    stream.len = 0;
    stream.end_of_frame = HI_FALSE;
    stream.end_of_stream = HI_TRUE;
    stream.need_display = HI_TRUE;

    hi_vdec_pic_info outPicInfo;
    outPicInfo.width = 0;
    outPicInfo.height = 0;
    outPicInfo.width_stride = 0;
    outPicInfo.height_stride = 0;
    outPicInfo.pixel_format = (hi_pixel_format)0;
    outPicInfo.vir_addr = 0;
    outPicInfo.buffer_size = 0;
    // send frame
    APP_ERROR ret = hi_mpi_vdec_send_stream(vdecConfig_.channelId, &stream, &outPicInfo, -1);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to send frame with error code: " << std::hex << ret << ".";
        return APP_ERR_ACL_BAD_ALLOC;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVdecGetFrame(hi_video_frame_info &frame, bool &decResult, uint32_t count)
{
    hi_vdec_stream stream;
    hi_vdec_supplement_info stSupplement {};
    APP_ERROR ret = hi_mpi_vdec_get_frame(vdecConfig_.channelId, &frame, &stSupplement, &stream, TIME_OUT);
    if ((unsigned)ret == HI_ERR_VDEC_BUF_EMPTY) {
        LogWarn << "Vdec buff empty.";
        return ret;
    } else if ((unsigned)ret != HI_ERR_VDEC_BUF_EMPTY && ret != APP_ERR_OK) {
        LogError << "Vdec get frame failed, ret = " << std::hex << ret;
        HiVdecRelease(frame, stream);
        return ret;
    } else {
        int32_t frameFlag = frame.v_frame.frame_flag;
        if (frameFlag == 0) {
            decResult = true;
            ret = PrepareToWrite(frame, count);
            if (ret != APP_ERR_OK) {
                LogError << "PrepareToWrite failed, ret = " << ret;
                HiVdecRelease(frame, stream);
                return ret;
            }
            HiVdecRelease(frame, stream);
        } else {
            decResult = false;
            HiVdecRelease(frame, stream);
            LogWarn << "Get frame successfully, but failed to decode, frameFlag = " << frameFlag;
        }
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::HiVdecRelease(hi_video_frame_info &frame, hi_vdec_stream &stream)
{
    APP_ERROR ret = hi_mpi_dvpp_free(stream.addr); // release input dev buff
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_dvpp_free failed, ret = " << std::hex << ret;
        return ret;
    }
    ret = hi_mpi_dvpp_free(frame.v_frame.virt_addr[0]); // release output dev buff
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_dvpp_free failed, ret = " << std::hex << ret;
        return ret;
    }
    ret = hi_mpi_vdec_release_frame(vdecConfig_.channelId, &frame);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_release_frame failed, ret = " << std::hex << ret;
        return ret;
    }
    return APP_ERR_OK;
}

APP_ERROR DvppCommon::VdecChnQueryStatus(bool &chnStatus)
{
    hi_vdec_chn_status status;
    APP_ERROR ret = hi_mpi_vdec_query_status(vdecConfig_.channelId, &status);
    if (ret != APP_ERR_OK) {
        LogError << "hi_mpi_vdec_query_status failed, ret = " << std::hex << ret;
        return ret;
    }
    if ((status.left_decoded_frames == 0) && (status.left_stream_bytes == 0)) {
        chnStatus = true;
    } else {
        chnStatus = false;
    }
    return APP_ERR_OK;
}

void DvppCommon::SetDeviceId(int32_t deviceId)
{
    deviceId_ = deviceId;
}

void DvppCommon::SetChnId(int32_t chnId)
{
    chnId_ = chnId;
}

void DvppCommon::SetContext(aclrtContext &context)
{
    context_ = context;
}

aclrtContext DvppCommon::GetContext()
{
    return context_;
}

void DvppCommon::SetRunFlag(bool runFlag)
{
    runFlag_ = runFlag;
}

bool DvppCommon::GetRunFlag()
{
    return runFlag_;
}

/*
 * @description: Get picture width and height and number of channels from image data
 * @param: data specifies the memory to store the image data
 * @param: dataSize specifies the size of the image data
 * @param: width is used to save the image width
 * @param: height is used to save the image height
 * @param: components is used to save the number of channels
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetJpegImageInfo(const void *data, uint32_t dataSize, uint32_t &width, uint32_t &height,
                                       int32_t &components)
{
    uint32_t widthTmp;
    uint32_t heightTmp;

    hi_vdec_stream stStream {};
    stStream.len = dataSize;
    stStream.addr = (uint8_t *)data;
    stStream.end_of_frame = HI_TRUE;
    stStream.end_of_stream = HI_FALSE;
    stStream.need_display = HI_TRUE;
    hi_img_info stImgInfo {};
    APP_ERROR ret = hi_mpi_dvpp_get_image_info(HI_PT_JPEG, &stStream, &stImgInfo);
    widthTmp = stImgInfo.width;
    heightTmp = stImgInfo.height;

    if (ret != APP_ERR_OK) {
        LogError << "Failed to get image info of jpeg, ret = " << ret << ".";
        return ret;
    }
    if (widthTmp > MAX_JPEGD_WIDTH || widthTmp < MIN_JPEGD_WIDTH) {
        LogError << "Input width is invalid, not in [" << MIN_JPEGD_WIDTH << ", " << MAX_JPEGD_WIDTH << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (heightTmp > MAX_JPEGD_HEIGHT || heightTmp < MIN_JPEGD_HEIGHT) {
        LogError << "Input height is invalid, not in [" << MIN_JPEGD_HEIGHT << ", " << MAX_JPEGD_HEIGHT << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    width = widthTmp;
    height = heightTmp;

    return APP_ERR_OK;
}

/*
 * @description: Transfer data from host to device
 * @param: imageInfo specifies the image data on the host
 * @param: jpegInput is used to save the buffer and its size which is allocate on the device
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::TransferImageH2D(const RawData& imageInfo, const std::shared_ptr<DvppDataInfo>& jpegInput) const
{
    // Check image buffer size validity
    if (imageInfo.lenOfByte <= 0) {
        LogError << "The input buffer size on host should not be empty.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    uint8_t* inDevBuff = nullptr;
    APP_ERROR ret = hi_mpi_dvpp_malloc(deviceId_, (void **)&inDevBuff, imageInfo.lenOfByte);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc " << imageInfo.lenOfByte << " bytes on dvpp, ret = " << ret << ".";
        return ret;
    }

    // Copy the image data from host to device
    ret = aclrtMemcpy(inDevBuff, imageInfo.lenOfByte, imageInfo.data.get(), imageInfo.lenOfByte,
                      ACL_MEMCPY_HOST_TO_DEVICE);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to copy " << imageInfo.lenOfByte << " bytes from host to device, ret = " << ret << ".";
        RELEASE_DVPP_DATA(inDevBuff);
        return ret;
    }

    jpegInput->data = inDevBuff;
    jpegInput->dataSize = imageInfo.lenOfByte;

    return APP_ERR_OK;
}

/*
 * @description: Get the aligned width, height, and data size of the input image
 * @param: inputImage specifies the input image information
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetJpegEncodeStrideSize(std::shared_ptr<DvppDataInfo> &inputImage)
{
    uint32_t inputWidth = inputImage->width;
    uint32_t inputHeight = inputImage->height;
    uint32_t widthStride;
    uint32_t heightStride;
    uint32_t encodedBufferSize;

    hi_pixel_format format = inputImage->format;
    // Align up the input width and height and calculate buffer size of encoded input file
    if (format == HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420 || format == HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420) {
        widthStride = DVPP_ALIGN_UP(inputWidth, JPEGE_STRIDE_WIDTH);
        heightStride = DVPP_ALIGN_UP(inputHeight, JPEGE_STRIDE_HEIGHT);
        encodedBufferSize = widthStride * heightStride * YUV_BYTES_NU / YUV_BYTES_DE;
    } else if (format == HI_PIXEL_FORMAT_YUYV_PACKED_422 || format == HI_PIXEL_FORMAT_UYVY_PACKED_422 ||
               format == HI_PIXEL_FORMAT_YVYU_PACKED_422 || format == HI_PIXEL_FORMAT_VYUY_PACKED_422) {
        widthStride = DVPP_ALIGN_UP(inputWidth * YUV422_WIDTH_NU, JPEGE_STRIDE_WIDTH);
        heightStride = DVPP_ALIGN_UP(inputHeight, JPEGE_STRIDE_HEIGHT);
        encodedBufferSize = widthStride * heightStride;
    } else {
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (encodedBufferSize == 0) {
        LogError << "Input host buffer size is empty.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    inputImage->widthStride = widthStride;
    inputImage->heightStride = heightStride;
    inputImage->dataSize = encodedBufferSize;
    return APP_ERR_OK;
}
#endif